/*!***************************************************
 * @file: client_management_app.c
 * @brief: 用户管理功能的逻辑应用代码
 * @author: ITHAO 
 * @date: 2018.12.10
 * @note: 
 ****************************************************/
#include <string.h>

#include "client_management.h"
#include "comm_protocol_core.h"
#include "func.h"

static bool send_status_to_friend(char *account, bool status,
                                  struct online_user *online_user);
static bool get_unsend_msg(char *account, int clientfd);

/**
 * @brief	专门处理用户管理部分的请求   
 * @param	data_packet--需要被处理的数据
 *          clientfd--通信fd
 *          online_user--在线用户数据
 * @return	void
 * @note:   此函数被工作线程调用
 *          这个函数的功能应该再封装一层
 */
void client_man_app(struct data_packet data_packet,
                       int clientfd, struct online_user *online_user)
{
    int ret = 0;
    struct data_packet recv_data_packet;
    struct comm_core comm;
    struct login_info login_info;
    struct data_packet *send_data_packet;
    char remove_account[ACCOUNT_LENGTH];

    recv_data_packet = data_packet;
    comm_core_init(&comm);
    comm.dev.cmd = COMM_SEND;
    send_data_packet = &comm.data.send_data_packet;
    send_data_packet->cmd_type = recv_data_packet.cmd_type;
    send_data_packet->cmd = recv_data_packet.cmd;
    comm.data.sendfd = clientfd;

    switch (recv_data_packet.cmd) {
        /*请求登陆命令*/
        case LOGIN_CMD:
            /*登陆需要两个数据*/
            if(!recv_data_packet.data[0] || !recv_data_packet.data[1]) {
                send_data_packet->ack = SHORT_OF_DATA_ACK;
                break;
            }

            strncpy(login_info.account, 
                    (char *)recv_data_packet.data[0], 
                    ACCOUNT_LENGTH);
            strncpy(login_info.password, 
                    (char *)recv_data_packet.data[1], 
                    PASSWORD_LENGTH);
            /*判断用户是否已经登陆*/        
            ret = is_online_user(login_info.account, 
                                    online_user, NULL);
            if (ret == TRUE) {
                send_data_packet->ack = LOGIN_ALREADY_LGOIN_ACK;
                break;
            }
            /*进行登陆操作*/
            ret = user_login(&login_info);
            if (ret == ACCOUNT_NOT_EXIXT) {
                send_data_packet->ack = LOGIN_ACCOUNT_NOT_EXIXT_ACK;
                break;
            }
            if (ret == PASSWORD_INCORRECT) {
                send_data_packet->ack = LOGIN_PASSWORD_ERR_ACK;
                break;
            }
            /*添加到在线用户链表*/
            ret = add_online_user(&login_info, clientfd,
                                    online_user);
            if (ret == OVER_ONLINE_USER_LIMIT) {
                send_data_packet->ack = LOGIN_NUM_TOO_MUCH_ACK;
                break;
            }

            send_data_packet->ack = LOGIN_SUCCESS_ACK;
            send_data_packet->data[0] = (uint8 *)login_info.name;
            /*通知好友自己上线了*/
            send_status_to_friend(login_info.account, 1, online_user);

            break;

        /*注册命令*/
        case REGISTER_CMD:
            /*登陆需要三个数据*/
            if (!recv_data_packet.data[0] || 
                !recv_data_packet.data[1] || 
                !recv_data_packet.data[2]) {
                send_data_packet->ack = SHORT_OF_DATA_ACK;
                break;
            }
            strncpy(login_info.name, 
                    (char *)recv_data_packet.data[0], 
                    NAME_LENGTH);
            strncpy(login_info.account, 
                    (char *)recv_data_packet.data[1], 
                    ACCOUNT_LENGTH);
            strncpy(login_info.password, 
                    (char *)recv_data_packet.data[2], 
                    PASSWORD_LENGTH);
            /*进行注册*/
            ret = user_register(&login_info);
            if (ret == ACCOUNT_EXIXT) {
                send_data_packet->ack = REGISTER_ACCOUNT_EXIST_ACK;
                break;
            }
            if (ret == TRUE) {
                send_data_packet->ack = REGISTER_SUCCESS_ACK;
            }

            break;

        /*注销命令*/
        case LOGOUT_CMD:
        case LOGOUT_BY_SERVER_CMD:
            /*将用户从在线链表中移除*/
            ret = remove_online_user(clientfd,
                                     remove_account, online_user);
            if (ret == FAIL)
                send_data_packet->ack = LOGOUT_NOT_ONLINE_ACK;
            else {
               send_data_packet->ack = LOGOUT_SUCCESS_ACK;
                /*通知好友自己下线了*/
                send_status_to_friend(remove_account, 0, online_user);            
            }

            break;

        /*判断用户是否在线*/
        case CHECK_ONLINE_CMD:
            if (!recv_data_packet.data[0]) {
                send_data_packet->ack = SHORT_OF_DATA_ACK;
                break;
            }

            strncpy(login_info.account, 
                    (char *)recv_data_packet.data[0], 
                    ACCOUNT_LENGTH);

            ret = is_online_user(login_info.account,
                                   online_user, NULL);
            if (ret == FAIL) 
                send_data_packet->ack = CHECK_OFFLINE_ACK;
            else
                send_data_packet->ack = CHECK_ONLINE_ACK;

            break;

        default:
            send_data_packet->ack = WRONG_CMD_ACK;
            break;
    }

    /*如果是被服务器注销的就不用应答了，不然会报发送错误的*/
    if (recv_data_packet.cmd != LOGOUT_BY_SERVER_CMD)
        comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);

    /*获取未接收的消息,这个要在登陆应答完之后才获取，因为客户端应答了才能接收*/
    if (recv_data_packet.cmd == LOGIN_CMD)
        get_unsend_msg(login_info.account, clientfd); 

    comm_core_destory(&comm);
}

/**
 * @brief	通知好友当前用户上线或者下线   
 * @param	account--用户账号
 *          status--在线状态
 *          online_user--在线用户数据
 * @return	TRUE 发送成功
 *          FAIL 发送失败
 * @note:   
 */
static bool send_status_to_friend(char *account, bool status,
                                  struct online_user *online_user)
{
    int ret = 0;
    int count = 0;
    int friend_fd = -1;
    struct friend friend;
    struct comm_core comm;

    if (!account || !online_user)
        return FAIL;

    comm_core_init(&comm);
    comm.dev.cmd = COMM_SEND;
    comm.data.send_data_packet.cmd_type = FRIEND_CMD_TYPE;
    if (status)
        comm.data.send_data_packet.cmd = FRIEND_ONLINE_CMD;
    else    
        comm.data.send_data_packet.cmd = FRIEND_OFFLINE_CMD;

    /*获取好友表*/
    ret = load_friend(account, &friend);
    if (ret == TRUE) {
        for(count = 0 ; count < MAX_FRIEND_NUM; count++) {
            /*判断好友是否在线*/
            if (is_online_user(friend.info[count].account, 
                               online_user, &friend_fd) &&
                               friend.info[count].used_flag) {
                comm.data.sendfd = friend_fd;
                comm.data.send_data_packet.data[0] = (uint8 *)account;
                comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);     
            }
    
        }
    }

    comm_core_destory(&comm);
    return TRUE;
}

/**
 * @brief	获取离线时未接收的信息  
 * @param	account--用户账号
 *          clientfd--对应账号的fd
 * @return	TRUE 获取成功
 *          FAIL 获取失败
 * @note:   
 */
static bool get_unsend_msg(char *account, int clientfd)
{
    int ret = 0;
    struct comm_core comm;
    struct chat_packet chat_packet_tmp;
    struct data_packet *send_data_packet;

    if (!account)
        return FAIL;

    init_chat_packet(&chat_packet_tmp);
    comm_core_init(&comm);
    comm.dev.cmd = COMM_SEND;
    comm.data.send_data_packet.cmd_type = CHAT_CMD_TYPE;
    comm.data.sendfd = clientfd;
    send_data_packet = &comm.data.send_data_packet;

    do {
        ret = serch_chat_packet(account, &chat_packet_tmp);
        if (ret == TRUE) {
            send_data_packet->data[0] = (uint8 *)chat_packet_tmp.send_name;
            send_data_packet->data[1] = (uint8 *)chat_packet_tmp.send_account;
            send_data_packet->data[2] = (uint8 *)chat_packet_tmp.time;
            send_data_packet->data[3] = (uint8 *)chat_packet_tmp.msg;
            comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg); 
        }
    } while (ret == TRUE);

    comm_core_destory(&comm);
    return TRUE;
}